package pw.goyd.subscribe;

import org.apache.ibatis.session.SqlSession;
import org.jboss.netty.channel.Channel;
import pw.goyd.db.RedisUtil;
import pw.goyd.db.mapping.PlayerMapper;
import pw.goyd.game.api.CalcCards;
import pw.goyd.game.api.DBDone;
import pw.goyd.game.constant.enumcard.HandCardsWeight;
import pw.goyd.game.constant.setting.Command;
import pw.goyd.game.constant.setting.LuckyPool;
import pw.goyd.game.logic.*;
import pw.goyd.game.model.Card;
import pw.goyd.game.model.LuckyAward;
import pw.goyd.game.model.Player;
import pw.goyd.game.server.Log;
import pw.goyd.game.server.LoginList;
import pw.goyd.game.server.Server;
import pw.goyd.protocol.model.Response;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;

import java.util.ArrayList;
import java.util.List;

/**
 * 玩家扩展类
 *
 * @author StanWind
 */
public class PlayerCustomer extends Player implements
        Comparable<PlayerCustomer> {

    private BinaryRedisPubSubListener binaryRedisPubSubListener;
    // session channel
    private Channel channel;
    private long channelID;
    private Jedis jedis;
    // 游戏类
    private int roomID = -1;
    private boolean isReady = false;
    private List<Card> cards;
    private int declarer = 1;       // 抢庄倍数
    private int bet = -1;           // 玩家倍数
    // 牌型信息
    private Card resCard;           // 最大的牌
    private HandCardsWeight resType;// 点数
    private int changingGolds;      // 这把变化的coin

    //~~~~~~~~~~~~~~~订阅部分
    private Thread boardcastT;
    private Thread roomT;

    public PlayerCustomer() {
        cards = new ArrayList<>();
    }

    public long getChannelID() {
        return channelID;
    }

    public void setChannelID(long channelID) {
        this.channelID = channelID;
    }

    public int getChangingGolds() {
        return changingGolds;
    }

    public void setChangingGolds(int changingGolds) {
        this.changingGolds = changingGolds;
    }

    /**
     * 每局游戏结束
     */
    public void gameFinish() {
        setAll_times(getAll_times() + 1);   // 增加场次
        if (getChangingGolds() > 0)
            setWin_times(getWin_times() + 1);
        if (getGold() < 0)
            setGold(0);

        savePlayer();
        setChangingGolds(0);
    }

    public Card getResCard() {
        return resCard;
    }

    public void setResCard(Card resCard) {
        this.resCard = resCard;
    }

    public HandCardsWeight getResType() {
        return resType;
    }

    public void setResType(HandCardsWeight resType) {
        this.resType = resType;
    }

    public List<Card> getCards() {
        return cards;
    }

    public void setCards(List<Card> cards) {
        this.cards = cards;
    }

    public int getDeclarer() {
        return declarer;
    }

    public void setDeclarer(int declarer) {
        this.declarer = declarer;
    }

    public int getBet() {
        return bet;
    }

    public void setBet(int bet) {
        this.bet = bet;
    }

    public boolean isReady() {
        return isReady;
    }

    public void setReady(boolean isReady) {
        this.isReady = isReady;
    }

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
        setChannelID(channel.getId());
    }

    public int getRoomID() {
        return roomID;
    }

    public void setRoomID(int roomID) {
        this.roomID = roomID;
    }

    public Jedis getJedis() {
        return jedis;
    }

    public void setJedis(Jedis jedis) {
        this.jedis = jedis;
    }

    public BinaryRedisPubSubListener getBinaryRedisPubSubListener() {
        return binaryRedisPubSubListener;
    }

    public void setBinaryRedisPubSubListener(
            BinaryRedisPubSubListener binaryRedisPubSubListener) {
        this.binaryRedisPubSubListener = binaryRedisPubSubListener;
    }

    /**
     * 信息写入数据库
     */
    public void savePlayer() {
        DBDone.updatePlayer(this);
    }

    /**
     * 保存钻石
     */
    public void saveDiamond() {
        SqlSession session = Server.sessionFactory.openSession();
        PlayerMapper playerMapper = session.getMapper(PlayerMapper.class);
        playerMapper.updateDiamond(this);
        session.commit();
        session.close();
    }

    /**
     * 监听大厅
     */
    public void psub() {
        //TODO 长时间处于idel 可能Jedis Exception
        //阻塞监听线程
        boardcastT = new Thread(() -> {
            Log.getLogger().info(
                    "订阅---->" + "玩家[" + getNickname() + "] 订阅全局");
            try {
                byte[] b = "broadcast.*".getBytes();
                jedis.psubscribe(binaryRedisPubSubListener,
                        b);
            } catch (JedisException e) {
                Log.getLogger().error("idle long 未登陆？？psub", e);
                unsub();
                //boardcastT.start();
            } catch (ClassCastException e) {
                Log.getLogger().error("ClassCast", e);
                unsub();//全部取消订阅
            }

        });

        boardcastT.start();
    }

    /**
     * 监听房间
     */
    public void psubRoom() {
        // 阻塞监听线程
        roomT = new Thread(() -> {
            Log.getLogger().info(
                    "订阅---->" + "玩家[" + getNickname() + "] 进入房间[" + roomID
                            + "]");
            try {
                jedis.psubscribe(binaryRedisPubSubListener,
                        ("room." + roomID).getBytes());
            } catch (JedisException e) {
                Log.getLogger().error("idle long 未登陆－－psubroom", e);
                LoginList.removePlayer(this);
            } catch (ClassCastException e) {
                Log.getLogger().error("ClassCast", e);
                setRoomID(-1);
                unsubRoom();
            }
        });
        roomT.start();
    }


    /**
     * 取消订阅房间
     */
    public void unsubRoom() {
        Log.getLogger().info(
                "订阅---->" + "玩家[" + getNickname() + "] 退出房间[" + roomID + "]");
        unsub(("room." + roomID).getBytes());
        if (roomT != null) {
            roomT.stop();
            roomT = null;
        }


//        // TODO: 16/11/11 解决了由于取消订阅再次订阅导致的消息延迟到达 造成的客户端数据混乱 因此给客户端重新分配订阅客户端 但是资源消耗增加
//        Jedis temp = getJedis();
//        setJedis(RedisUtil.getJedis());
//        setBinaryRedisPubSubListener(new BinaryRedisPubSubListener());
//        Log.getLogger().info("Instance Finish");
//        RedisUtil.returnResource(temp);
    }

    /**
     * 取消所有订阅
     */
    public void unsub() {
        Log.getLogger().debug(
                "订阅---->玩家[" + getNickname() + "]取消所有订阅");
        if (binaryRedisPubSubListener != null)
            binaryRedisPubSubListener.unsubscribe();

        unsubRoom();
        if (boardcastT != null) {
            boardcastT.stop();
            boardcastT = null;
        }

        this.setBinaryRedisPubSubListener(null);
    }

    /**
     * 取消订阅频道 自己结束线程
     *
     * @param channel 频道名
     */
    protected void unsub(final byte[] channel) {
        if (binaryRedisPubSubListener != null)
            binaryRedisPubSubListener.unsubscribe(channel);
    }

    //~~~~~~~~~~~~~~~~~~~~游戏包部分

    /**
     * 不能进入房间
     */
    public void sendKickOutRoom() {
        Response response = new Response();
        KickOutRoomResponse kickOutRoomResponse = new KickOutRoomResponse();
        response.setData(kickOutRoomResponse.getBytes());
        response.setTime(0);
        response.setCmd(Command.CMD_KICKOUTROOM);

        //Log.getLogger().debug("Kick--->"+response.getAllBytes());
        this.getChannel().write(response);
    }

    /**
     * 发牌
     */
    public void sendCards() {
        DispatchResponse dispatchResponse = new DispatchResponse();
        dispatchResponse.setCards(this.cards);
        Response response = new Response();
        response.setCmd(Command.CMD_DISPATCH);
        response.setData(dispatchResponse.getBytes());
        response.setTime(0);
        this.channel.write(response);
    }

    /**
     * 发送最后一张牌
     */
    public void sendLastCard() {
        List<Card> lastCard = new ArrayList<>();
        lastCard.add(cards.get(cards.size() - 1));

        DispatchResponse dispatchResponse = new DispatchResponse();
        dispatchResponse.setCards(lastCard);
        Response response = new Response();
        response.setCmd(Command.CMD_DISPATCH);
        response.setData(dispatchResponse.getBytes());
        response.setTime(0);
        this.channel.write(response);
    }

    public void sendPlayerInfo() {
        PlayerInfoResponse playerInfoResponse = new PlayerInfoResponse();
        playerInfoResponse.setPlayer(this);
        Response response = new Response();
        response.setData(playerInfoResponse.getBytes());
        response.setTime(0);
        response.setCmd(Command.CMD_PLAYERINFO);
        this.getChannel().write(response);
    }

    /**
     * 推送分数
     */
    public void sendDeclarer() {
        Response response = new Response();
        DeclarerResponse declarerResponse = new DeclarerResponse();
        declarerResponse.setPlayerCustomer(this);
        response.setCmd(Command.CMD_DECLARER);
        response.setData(declarerResponse.getBytes());
        response.setTime(0);
        RedisUtil.pubRoom(this.getRoomID(), response.getAllBytes());
    }

    /**
     * 推送倍数
     */
    public void sendBet() {
        Response response = new Response();
        BetResponse betResponse = new BetResponse();
        betResponse.setPlayer(this);
        response.setCmd(Command.CMD_BET);
        response.setData(betResponse.getBytes());
        response.setTime(0);
        RedisUtil.pubRoom(this.getRoomID(), response.getAllBytes());
    }

    /**
     * 计算牌型
     */
    public void calcCards() {
        this.setResCard(CalcCards.calcMaxCard(this.getCards()));
        this.setResType(CalcCards.calcType(this.getCards()));
        Log.getLogger().info(
                getResType().name() + "----" + getResCard().weight);
    }

    /**
     * push to room broadcast and judge the room state
     */
    public void sendLeaveRoom() {
        Response response = new Response();
        ExitRoomResponse exitRoomResponse = new ExitRoomResponse();
        exitRoomResponse.setAccount_id(getAccount_id());
        response.setCmd(Command.CMD_EXITROOM);
        response.setData(exitRoomResponse.getBytes());
        response.setTime(0);

        if (roomID != -1) {
            RedisUtil.pubRoom(roomID, response.getAllBytes());
        }
    }

    @Override
    public String toString() {

        return "Jedis State:" + jedis.isConnected() + " NickName:"
                + getNickname() + " AccountID:" + getAccount_id() + " ChannelID:" + getChannelID()
                + " Channel State: open." + channel.isOpen() + " connected."
                + channel.isConnected() + " writable." + channel.isWritable()
                + " readable." + channel.isReadable();

    }

    @Override
    public int compareTo(PlayerCustomer o) {
        int compare = 0;
        // 比较牌型
        int result = this.getResType().ordinal() - o.getResType().ordinal();
        if (result > 0) {
            compare = 1;
        } else if (result < 0) {
            compare = -1;
        } else if (result == 0) {
            // 比较最大牌
            result = this.getResCard().compareTo(o.getResCard());
            if (result > 0) {
                compare = 1;
            } else if (result < 0) {
                compare = -1;
            }
        }

        return compare;
    }

    public void saveLuckyAward(LuckyAward luckyAward) {
        if (luckyAward.getLuckyType() == LuckyPool.LuckyType.Gold) {
            setGold(getGold() + luckyAward.getValue());
            savePlayer();
        } else if (luckyAward.getLuckyType() == LuckyPool.LuckyType.Diamond) {
            setDiamond(getDiamond() + luckyAward.getValue());
            savePlayer();
        }
    }


}